// 四、手写实现 MyPromise 源码
// 要求：尽可能还原 Promise 中的每一个API，并通过注释的方式描述思路和原理。

//  1. Promise 是一个类
//    1. 接收一个执行器函数 executor
//      1. 该函数是立即执行的
//      2. 该函数接收两个参数 resolve 和 reject

//  2. 三种状态
//    1. 等待 pending
//    2. 成功 fulfilled
//    3. 失败 rejected
//    4. 状态一经确定 就不可改变

//  3. then 方法
//    1. 接收两个参数
//      1. 成功回调 resovleCallback
//      2. 失败回调 rejectCallback
//    2. 用来判断状态，成功 -> resovleCallback 失败 -> rejectCallback
//    3. resovleCallback 参数 value 是 resolve 被调用时传递的参数
//    4. rejectCallback 参数 reason 是 reject 被调用时传递的参数

//  4. 处理异步情况
//    1. resolve 或 reject 没有立即执行
//    2. then 开始执行 此时还是 pending 状态 需存储 then 的两个参数 直到 resolve 或 reject 被执行
//    3. successCallback
//    4. failCallback

//  5. 处理多次 then 调用情况
//    1. 每一次 then 的回调都该被保存起来
//    2. 先调的先存，successCallback 和 failCallback 使用数组

//  6. then 的链式调用
//    1. then 返回 Promise 对象
//    2. 当前 then 方法成功或失败的返回值的类型
//      1. 普通值 会被传递给下一个 then 回调作为入参，成功的给成功，失败的给失败
//      2. promise resolve 执行时的参数会被传递给下一个 then 中的成功回调作为入参
//      3. 为自己时，会报错
//      4. then 的参数为可选 即使 then 不传参 参数也会被一直传递下去

//  7. 错误捕获
//    1. 构造器错误
//    2. then 方法错误

//  8. 【静态方法】all 解决异步并发问题
//    1. 参数为数组，数组内可以是 promise 也可以是普通值
//    2. 按照异步代码调用的顺序得到异步代码的结果（即使是有 setTimeout）
//    3. 返回 promise 对象
//    4. 最终状态 所有成功才成功 一个失败就失败

//  9. 【静态方法】resolve
//    1. 接收一个参数 普通值 或 promise
//    2. 返回一个 promise 普通值进行封装 promise 直接返回

//  10. finally
//    1. 接收一个函数
//    2. 返回 promise
//    3. 无论失败还是成功，都会执行接收的函数

//  11. catch
//    1. 捕获一个未写失败回调的 promise 的错误
//    2. 接收一个函数
//    3. 返回 promise

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch(e) {
      this.reject(e)
    }
  }

  // 状态，默认为 PENDING
  status = PENDING

  // resovleCallback 参数
  value = undefined

  // rejectCallback 参数
  reason = undefined

  // 异步时被暂存的成功回调
  successCallback = []

  // 异步时被暂存的失败回调
  failCallback = []

  // 用来改变状态的，pending -> fulfilled
  resolve = (resovleParams) => {
    // 状态一经确定，就不可改变
    if (this.status !== PENDING) return;
    // 更改为成功态
    this.status = FULFILLED
    // 保存成功参数
    this.value = resovleParams
    // 异步执行
    while(this.successCallback.length) this.successCallback.shift()()
  }

  // 用来改变状态的，pending -> rejected
  reject = (rejectParams) => {
    // 状态一经确定，就不可改变
    if (this.status !== PENDING) return;
    // 更改为失败态
    this.status = REJECTED
    // 保存失败参数
    this.reason = rejectParams
    // 异步执行
    while(this.failCallback.length) this.failCallback.shift()()
  }

  then = (resovleCallback, rejectCallback) => {
    resovleCallback = resovleCallback ? resovleCallback : val => val
    rejectCallback = rejectCallback ? rejectCallback : rea => rea
    const promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 仅仅是为了 promise 在传递时已经存在
        setTimeout(() => {
          try {
            const s = resovleCallback(this.value)
            judgeReturnType(promise, s, resolve, reject, FULFILLED)
          } catch(e) {
            reject(e)
          }
        }, 0)

      } else if (this.status === REJECTED) {
        // 仅仅是为了 promise 在传递时已经存在
        setTimeout(() => {
          try {
            const f = rejectCallback(this.reason)
            judgeReturnType(promise, f, resolve, reject, REJECTED)
          } catch(e) {
            reject(e)
          }
        }, 0)
        
      } else {
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              const s = resovleCallback(this.value)
              judgeReturnType(promise, s, resolve, reject, FULFILLED)
            } catch(e) {
              reject(e)
            }
          }, 0)
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              const f = rejectCallback(this.reason)
              judgeReturnType(promise, f, resolve, reject, REJECTED)
            } catch(e) {
              reject(e)
            }
          }, 0)
        })
      }
    })
    return promise
  }

  static all(array) {
    return new MyPromise((resolve, reject) => {
      let result = []
      let resultLength = 0
      for(let i = 0; i < array.length; i++) {
        const item = array[i]
        if (item instanceof MyPromise) {
          // item 类型为 promise，得到结果，放进 result
          item.then(value => {
            result[i] = value
            resultLength++
            if (resultLength === array.length) resolve(result)
          }, reason => reject(reason) )
        } else {
          // item 类型为普通值，直接放进 result
          result[i] = item
          resultLength++
          if (resultLength === array.length) resolve(result)
        }
      }
    })
  }

  static resolve(value) {
    if (value instanceof MyPromise) {
      return value
    } else {
      return new MyPromise((resolve, reject) => {
        resolve(value)
      })
    }
  }

  finally = (fn) => {
    return this.then(val => {
      return MyPromise.resolve(fn()).then(() => val)
    }, reason => {
      return MyPromise.resolve(fn()).then(() => { throw reason })
    })
  }

  catch = (fn) => {
    return this.then(undefined, fn)
  }
}

function judgeReturnType(val2, val, resolve, reject, status) {
  if(val2 === val) {
    reject(new TypeError('error。。。'))
    return;
  }
  if(val instanceof MyPromise) {
    val.then(resolve, reject)
  } else {
    // 不管是成功回调还是失败回调，只要返回普通值，就会传递到下个成功入参中(这里有问题,应该是下面这样)
    if(status === FULFILLED) {
      resolve(val)
    } else if(status === REJECTED) {
      reject(val)
    }
  }
}

module.exports = MyPromise